package com.alkaid.commons.utils;

import com.alibaba.fastjson.JSON;
import com.alkaid.commons.entity.Authority;
import com.alkaid.commons.entity.User;
import com.alkaid.commons.ex.TokenErrorException;
import com.alkaid.commons.ex.TokenExpiredException;
import io.jsonwebtoken.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;

/**
 * @author Kylin
 */
public class JwtUtils {
    private User user;
    /**
     * 密钥
     */
    public static final String SECRET = "alkaid-secret";
    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 生成jwt
     *
     * @param user       用户信息
     * @param isRemember 是否记住我
     * @return jwt
     */
    public String createJwt(User user, boolean isRemember) {
        // 过期时间 记住我-3天 未勾选-4小时
        int during = isRemember ? 3600 * 24 * 3 : 3600 * 4;
        int refresh = isRemember ? 3600 * 24 : 3600 * 4;
        List<Authority> authorities = user.getAuthorities();
        Map<String, Date> auth = new HashMap<>();
        for (Authority authority : authorities) {
            String name = authority.getAuthority();
            Date expire = authority.getExpiredTime();
            auth.put(name, expire);
        }
        // 获取当前时间并转换为北京时区时间（+8小时）
        Instant now = Instant.now();
        String jwt = Jwts.builder()
                .setId(user.getId().toString())
                // 设置当前用户是谁
                .setSubject(user.getUsername())
                // 设置开始的有效期
                .setIssuedAt(Date.from(now))
                // 设置刷新过期时间为当前时间向后顺延 during s
                .setExpiration(Date.from(now.plusSeconds(during)))
                // 过期时间
                .claim("refresh", Date.from(now.plusSeconds(refresh)))
                // 用户权限
                .claim("auth", auth)
                // 设置签名的算法和密钥值
                .signWith(SignatureAlgorithm.HS256, SECRET.getBytes())
                .compact();
        return TOKEN_PREFIX + jwt;
    }

    /**
     * 解析jwt
     *
     * @param jwt jwt
     * @return jwt是否需要更新
     */
    @SuppressWarnings("unchecked")
    public boolean parseJwt(String jwt) {
        jwt = jwt.replace(JwtUtils.TOKEN_PREFIX, "");
        try {
            Claims claims = (Claims) Jwts.parser()
                    .setSigningKey(JwtUtils.SECRET.getBytes())
                    .parse(jwt)
                    .getBody();
            this.user = new User();
            this.user.setId(Long.parseLong(claims.getId()));
            this.user.setUsername(claims.getSubject());
            List<Authority> authorities = new LinkedList<>();
            Map<String, Long> map = (Map<String, Long>) claims.get("auth");
            for (String key : map.keySet()) {
                Authority authority = new Authority();
                authority.setAuthority(key);
                authority.setExpiredTime(new Date(map.get(key)));
                authorities.add(authority);
            }
            this.user.setAuthorities(authorities);
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
            Date expire = new Date((Long) claims.get("refresh"));
            Date now = new Date();
            return now.getTime() > expire.getTime();
        } catch (ExpiredJwtException e) {
            throw new TokenExpiredException("登录过期");
        } catch (JwtException e) {
            throw new TokenErrorException("登录异常");
        }
    }

    /**
     * 将用户权限转为字符串
     *
     * @param authorities 用户权限列表
     * @return 用户权限
     */
    public static String parseAuthorities(List<Authority> authorities) {
        StringBuilder auth = new StringBuilder();
        for (Authority authority : authorities) {
            auth.append(authority.getAuthority());
            auth.append(",");
        }
        int length = auth.length();
        auth.delete(length - 1, length);
        return auth.toString();
    }

    /**
     * 发送data到前端
     *
     * @param resp response
     * @param data 发送数据
     */
    public static void responseStatus(HttpServletResponse resp, Object data) throws IOException {
        //处理编码方式，防止中文乱码的情况
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out = resp.getWriter();
        // 对象转json传输给前端
        out.write(JSON.toJSONString(data));
        out.flush();
        out.close();
    }

    public User getUser() {
        return user;
    }
}
